
/*    This file is part of the Managarm operating system.
 *   Copyright (C) 2007, 2008, 2009  Alexander van der Grinten
 *
 *   This program is free software: you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation, either version 3 of the License, or
 *   (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program.  If not, see <http://www.gnu.org/licenses/>. */

package org.managarm.jmanacclib.generic;

import java.util.ArrayList;
import java.util.TreeMap;
import java.util.TreeSet;

public class GnFrameInfo {
	// stores frame jump information
	public final static class JumpInfo {
		public final static int MODE_STANDARD = 1;
		public final static int MODE_EXCEPTION = 2;
		
		public final int jumpMode;
		public final GnFrameInfo sourceFrame;
		public final int sourceIp;
		
		public JumpInfo(final int mode,
				GnFrameInfo sourceframe, int sourceip) {
			this.jumpMode = mode;
			this.sourceFrame = sourceframe;
			this.sourceIp = sourceip;
		}
	}
	
	public final GnFrame frameObject;
	
	// arguments of this frame
	public final TreeSet<Integer> argSet =
			new TreeSet<Integer>();
	public final ArrayList<Integer> argList =
		new ArrayList<Integer>();
	public final TreeMap<Integer, Integer> constructed =
			new TreeMap<Integer, Integer>();
	public final ArrayList<JumpInfo> jumps =
			new ArrayList<JumpInfo>();
	public final boolean argsLocked;
	
	public GnFrameInfo(final GnFrame frame, boolean argslocked) {
		this.frameObject = frame;
		this.argsLocked = argslocked;
	}
	
	// determines all (direct) arguments and constructed registers of this frame
	public final void analyzeArguments() {
		// the exception object becomes the first argument
		if(frameObject.exception != -1)
			addArgument(frameObject.exception);
		
		for(int i = 0; i < frameObject.instructions.size(); i++) {
			final GnInst opcode = frameObject.instructions.get(i);
			
			if(opcode instanceof GnInst.InstLoad) {
				final GnInst.InstLoad inst = (GnInst.InstLoad)opcode;
				if(!constructed.containsKey(inst.register))
					constructed.put(inst.register, i);
			}else if(opcode instanceof GnInst.InstReturn) {
				final GnInst.InstReturn inst = (GnInst.InstReturn)opcode;
				if(inst.register != 0
						&& !constructed.containsKey(inst.register))
					addArgument(inst.register);
			}else if(opcode instanceof GnInst.InstBinOperation) {
				final GnInst.InstBinOperation inst
						= (GnInst.InstBinOperation)opcode;
				if(!constructed.containsKey(inst.left))
					addArgument(inst.left);
				if(!constructed.containsKey(inst.right))
					addArgument(inst.right);
				if(!constructed.containsKey(inst.register))
					constructed.put(inst.register, i);
			}else if(opcode instanceof GnInst.InstUnOperation) {
				final GnInst.InstUnOperation inst
						= (GnInst.InstUnOperation)opcode;
				if(!constructed.containsKey(inst.operand))
					addArgument(inst.operand);
				if(!constructed.containsKey(inst.register))
					constructed.put(inst.register, i);
			}else if(opcode instanceof GnInst.InstClone) {
				final GnInst.InstClone inst	= (GnInst.InstClone)opcode;
				if(!constructed.containsKey(inst.operand))
					addArgument(inst.operand);
				if(!constructed.containsKey(inst.register))
					constructed.put(inst.register, i);
			}else if(opcode instanceof GnInst.InstJump) {
				final GnInst.InstJump inst = (GnInst.InstJump)opcode;
				if(inst.register != 0
						&& !constructed.containsKey(inst.register))
					addArgument(inst.register);
			}else if(opcode instanceof GnInst.InstInvoke) {
				final GnInst.InstInvoke inst = (GnInst.InstInvoke)opcode;
				if(inst.object != 0
						&& !constructed.containsKey(inst.object))
					addArgument(inst.object);
				for(int j = 0; j < inst.getArgumentCnt(); j++) {
					if(!constructed.containsKey(inst.getArgument(j)))
						addArgument(inst.getArgument(j));
				}
				if(!constructed.containsKey(inst.result))
					constructed.put(inst.result, i);
			}else if(opcode instanceof GnInst.InstCreateObject) {
				final GnInst.InstCreateObject inst
						= (GnInst.InstCreateObject)opcode;
				if(!constructed.containsKey(inst.register))
					constructed.put(inst.register, i);
			}else if(opcode instanceof GnInst.InstCreateArray) {
				final GnInst.InstCreateArray inst
						= (GnInst.InstCreateArray)opcode;
				if(!constructed.containsKey(inst.size))
					addArgument(inst.size);
				if(!constructed.containsKey(inst.register))
					constructed.put(inst.register, i);
			}else if(opcode instanceof GnInst.InstArrAccess) {
				final GnInst.InstArrAccess inst
						= (GnInst.InstArrAccess)opcode;
				if(!constructed.containsKey(inst.array))
					addArgument(inst.array);
				if(!constructed.containsKey(inst.index))
					addArgument(inst.index);
				if(inst.operator == GnInst.InstArrAccess.OP_STORE) {
					if(!constructed.containsKey(inst.operand))
						addArgument(inst.operand);
				}else if(inst.operator == GnInst.InstArrAccess.OP_LOAD) {
					if(!constructed.containsKey(inst.operand))
						constructed.put(inst.operand, i);
				}else{
					System.err.println("analyzeFrame(): Invalid operator " +
							"for GnInst.InstFldAccess");
					System.exit(1);
				}
			}else if(opcode instanceof GnInst.InstFldAccess) {
				final GnInst.InstFldAccess inst
						= (GnInst.InstFldAccess)opcode;
				if(inst.object != 0
						&& !constructed.containsKey(inst.object))
					addArgument(inst.object);
				if(inst.operator == GnInst.InstFldAccess.OP_STORE) {
					if(!constructed.containsKey(inst.operand))
						addArgument(inst.operand);
				}else if(inst.operator == GnInst.InstFldAccess.OP_LOAD) {
					if(!constructed.containsKey(inst.operand))
						constructed.put(inst.operand, i);
				}else{
					System.err.println("analyzeFrame(): Invalid operator " +
							"for GnInst.InstFldAccess");
					System.exit(1);
				}
			}else if(opcode instanceof GnInst.InstCast) {
				final GnInst.InstCast inst = (GnInst.InstCast)opcode;
				if(!constructed.containsKey(inst.source))
					addArgument(inst.source);
				if(!constructed.containsKey(inst.dest))
					constructed.put(inst.dest, i);
			}else if(opcode instanceof GnInst.InstInstanceof) {
				final GnInst.InstInstanceof inst
						= (GnInst.InstInstanceof)opcode;
				if(!constructed.containsKey(inst.object))
					addArgument(inst.object);
				if(!constructed.containsKey(inst.register))
					constructed.put(inst.register, i);
			}else if(opcode instanceof GnInst.InstDlgConstruct) {
				final GnInst.InstDlgConstruct inst
						= (GnInst.InstDlgConstruct)opcode;
				if(inst.object != 0)
					if(!constructed.containsKey(inst.object))
						addArgument(inst.object);
				if(!constructed.containsKey(inst.result))
					constructed.put(inst.result, i);
			}else if(opcode instanceof GnInst.InstCatch) {
				final GnInst.InstCatch inst = (GnInst.InstCatch)opcode;
				if(!constructed.containsKey(inst.exception))
					constructed.put(inst.exception, i);
			}else if(opcode instanceof GnInst.InstThrow) {
				final GnInst.InstThrow inst = (GnInst.InstThrow)opcode;
				if(!constructed.containsKey(inst.exception))
					addArgument(inst.exception);
			}else if(opcode instanceof GnInst.InstLeave) {
				// ignore this opcode
			}else if(opcode instanceof GnInst.InstXNatMthAddress) {
				final GnInst.InstXNatMthAddress inst
						= (GnInst.InstXNatMthAddress)opcode;
				if(!constructed.containsKey(inst.dest))
					constructed.put(inst.dest, i);
			}else if(opcode instanceof GnInst.InstXNatSizeofObject) {
				final GnInst.InstXNatSizeofObject inst
						= (GnInst.InstXNatSizeofObject)opcode;
				if(!constructed.containsKey(inst.dest))
					constructed.put(inst.dest, i);
			}else if(opcode instanceof GnInst.InstXNatAddressCast) {
				final GnInst.InstXNatAddressCast inst
						= (GnInst.InstXNatAddressCast)opcode;
				if(!constructed.containsKey(inst.source))
					addArgument(inst.source);
				if(!constructed.containsKey(inst.dest))
					constructed.put(inst.dest, i);
			}else if(opcode instanceof GnInst.InstXNatAddressOf) {
				final GnInst.InstXNatAddressOf inst
						= (GnInst.InstXNatAddressOf)opcode;
				if(!constructed.containsKey(inst.object))
					addArgument(inst.object);
				if(!constructed.containsKey(inst.result))
					constructed.put(inst.result, i);
			}else if(opcode instanceof GnInst.InstXNatMakeDelegate) {
				final GnInst.InstXNatMakeDelegate inst
						= (GnInst.InstXNatMakeDelegate)opcode;
				if(!constructed.containsKey(inst.method))
					addArgument(inst.method);
				if(!constructed.containsKey(inst.object))
					addArgument(inst.object);
				if(!constructed.containsKey(inst.result))
					constructed.put(inst.result, i);
			}else if(opcode instanceof GnInst.InstXNatRefOperation) {
				final GnInst.InstXNatRefOperation inst
						= (GnInst.InstXNatRefOperation)opcode;
				if(!constructed.containsKey(inst.register))
					addArgument(inst.register);
			}else if(opcode instanceof GnInst.InstXAtomCas) {
				final GnInst.InstXAtomCas inst
						= (GnInst.InstXAtomCas)opcode;
				if(!constructed.containsKey(inst.object))
					addArgument(inst.object);
				if(!constructed.containsKey(inst.source))
					addArgument(inst.source);
				if(!constructed.containsKey(inst.value))
					addArgument(inst.value);
				if(inst.dest != 0)
					if(!constructed.containsKey(inst.dest))
						constructed.put(inst.dest, i);
			}else if(opcode instanceof GnInst.InstXPerfArrayCopy) {
				final GnInst.InstXPerfArrayCopy inst
						= (GnInst.InstXPerfArrayCopy)opcode;
				if(!constructed.containsKey(inst.dest))
					addArgument(inst.dest);
				if(!constructed.containsKey(inst.source))
					addArgument(inst.source);
				if(!constructed.containsKey(inst.destOffset))
					addArgument(inst.destOffset);
				if(!constructed.containsKey(inst.sourceOffset))
					addArgument(inst.sourceOffset);
				if(!constructed.containsKey(inst.size))
					addArgument(inst.size);
			}else{
				System.err.println("analyzeFrame(): Invalid instruction");
				System.exit(1);
			}
		}
	}
	
	// adds an argument to "info"
	public final void addArgument(final int register) {
		addArgument(register, false);
	}
	public final void addArgument(final int register, final boolean force) {
		if(argSet.contains(register))
			return;
		if(argsLocked && force == false)
			throw new UnsupportedOperationException("Uninitialized " +
					"register " + register);
		argList.add(register);
		argSet.add(register);
	}
	
	// adds the argument "register" to the frame "info" if
	// "register" is not constructed before "instptr" and
	// adds it recursively to every frame that invokes "info".
	public final void addDependence(final int register, final int instptr) {
		if(argSet.contains(register))
			return;
		// do nothing if it is constructed before "instptr"
		final Integer cnstrip = constructed.get(register);
		if(cnstrip != null && cnstrip < instptr)
			return;
		
		addArgument(register);
		
		for(int i = 0; i < jumps.size(); i++) {
			final JumpInfo jump = jumps.get(i);
			jump.sourceFrame.addDependence(register, jump.sourceIp);
		}
	}
}
